import { APP_TYPE, WX } from '@/config'
import { isDebugMode } from '@/config/public'
import { rqtGetRefreshToken } from '@/services/common'
import { setAuthorization } from '@/utils/request'
import axios from 'axios'
import { Base64 } from 'js-base64'
import Cookie from 'js-cookie'

var _ = require('lodash')
var toLogin = undefined

// 请求错误忽略
const reqErrorIgnore = {
  paths: [
    '/provider/token/accesstoken',
    '/provider/ams/userPrivilege/workwechat/login'
  ],
  includes(url) {
    for (const path of this.paths) {
      if (url.includes(path)) {
        return true
      }
    }
    return false
  }
}

var isTokenFreshing = false
function fetchFreshToken(response, options) {
  console.log('fetchFreshToken => ')
  if (!isTokenFreshing) {
    isTokenFreshing = true
    console.log('rqtGetRefreshToken...')
    rqtGetRefreshToken()
      .then(res => {
        console.log('rqtGetRefreshToken success')
        const { store } = options
        const user = store.state.account.user
        const { accesstoken, refreshtoken } = res.headers
        user.refreshtoken = refreshtoken
        const expireAt = new Date(new Date().getTime() + 24 * 60 * 60 * 1000)
        store.commit('account/setUser', user)
        setAuthorization({ token: accesstoken, expireAt: new Date(expireAt) })
        onAccessTokenFetched()
        isTokenFreshing = false
      })
      .catch(error => {
        console.error('rqtGetRefreshToken error')
        onAccessTokenFetched(error)
        isTokenFreshing = false
      })
  }
  if (response.config.url.includes('token/refresh')) {
    return response
  }
  return new Promise((resolve, reject) => {
    const { config } = response
    addSubscriber(error => {
      if (error) {
        reject(error)
      } else {
        resolve(
          axios.request({
            url: config.url,
            method: config.method,
            data: config.data,
            headers: config.headers,
            timeout: config.timeout,
            withCredentials: config.withCredentials,
            xsrfHeaderName: config.xsrfHeaderName,
            xsrfCookieName: config.xsrfCookieName,
            params: config.params
          })
        )
      }
    })
  })
}
var subscribers = []
function onAccessTokenFetched(error) {
  subscribers.forEach(callback => {
    callback(error)
  })
  subscribers = []
}
function addSubscriber(callback) {
  subscribers.push(callback)
}

// 401拦截
const resp401 = {
  /**
   * 响应数据之前做点什么
   * @param response 响应对象
   * @param options 应用配置 包含: {router, i18n, store, message}
   * @returns {*}
   */
  onFulfilled(response, options) {
    const { message } = options
    if (response.status === 401) {
      message.error('无此接口权限')
    }
    return response
  },
  /**
   * 响应出错时执行
   * @param error 错误对象
   * @param options 应用配置 包含: {router, i18n, store, message}
   * @returns {Promise<never>}
   */
  onRejected(error, options) {
    const { message } = options
    message.error(error.message)
    return Promise.reject(error)
  }
}

const resp403 = {
  onFulfilled(response, options) {
    const { message } = options
    if (response.status === 403) {
      message.error(`请求被拒绝`)
    }
    return response
  }
}

// 业务拦截
const respCommon = {
  /**
   * 响应数据之前做点什么
   * @param response 响应对象
   * @param options 应用配置 包含: {router, i18n, store, message}
   * @returns {*}
   */
  onFulfilled(response, options) {
    if (
      response.data &&
      typeof response.data === 'string' &&
      response.request.responseType !== 'blob'
    ) {
      // response json data decrypt
      response.data = JSON.parse(Base64.decode(response.data))
    }
    const { data, config } = response
    const { message, router } = options
    if (isDebugMode) {
      console.log(
        '%c RESPONSE => ',
        'background: #222; color: #3CB371',
        `[url]: ${config.url}`,
        `[method]: ${config.method}`
      )
      if (response.request.responseType !== 'blob') {
        console.log(`[data]:`, data)
      }
    }
    if (data.code) {
      switch (data.code) {
        case 1000:
          return response
        // token 刷新成功
        case 3003:
          return response
        // access token 已经失效
        case 3001:
          return fetchFreshToken(response, options)
        // refresh token 已经失效
        case 3002:
        // redis 认证信息已经失效
        /* falls through */
        case 3004:
          if (!toLogin) {
            toLogin = _.throttle(
              function () {
                !reqErrorIgnore.includes(config.url) &&
                  message.warning('登录已失效，请重新登录')
                if (APP_TYPE === WX) {
                  router.replace('/wx/login')
                } else {
                  router.replace('/login')
                }
              },
              2000,
              { trailing: false }
            )
          }
          toLogin()
          return Promise.reject(response)
        case 5000:
        default:
          if (!reqErrorIgnore.includes(config.url)) {
            if (data.msg && data.data) {
              message.error(`【${data.msg}】：${data.data}`)
            } else {
              message.error(data.msg || JSON.stringify(data))
            }
          }
          return Promise.reject(response)
      }
    } else {
      return response
    }
  }
}

const reqCommon = {
  /**
   * 发送请求之前做些什么
   * @param config axios config
   * @param options 应用配置 包含: {router, i18n, store, message}
   * @returns {*}
   */
  onFulfilled(config, options) {
    const { message, store } = options
    const { url, xsrfCookieName } = config
    if (
      url.indexOf('login') === -1 &&
      url.indexOf('userPrivilege/getmenu') === -1 &&
      url.indexOf('modifypassword') === -1 &&
      xsrfCookieName &&
      !Cookie.get(xsrfCookieName)
    ) {
      message.warning('认证 token 已过期，请重新登录')
    } else if (url.indexOf('token/refresh') !== -1) {
      const refreshtoken = store.state.account.user.refreshtoken
      refreshtoken && (config.headers.refreshToken = refreshtoken)
    }
    const menuId = Cookie.get('MenuId')
    menuId && (config.headers.MenuId = menuId)
    const userId = store.state.account.user && store.state.account.user.userId
    userId && (config.headers.userId = userId)

    return config
  }
}

export default {
  request: [reqCommon], // 请求拦截
  response: [resp401, resp403, respCommon] // 响应拦截
}
